// Copyright (c) 2025 Inochi Amaoto

/* SPDX-License-Identifier: GPL-2.0 */

#ifndef __VIRTIO_VAPP_DTQ_H__
#define __VIRTIO_VAPP_DTQ_H__

int __init vio_dtq_init(void);
void vio_dtq_exit(void);
bool virtio_vapp_queue_work(struct work_struct* work);

static inline void virtio_vapp_dtq_assign_virtqueue(struct virtio_vapp_dtq* dtq, struct virtqueue** vqs)
{
  pr_info("dtq: assign rx %p\n", vqs[__VAPP_VQ_RX]);
  pr_info("dtq: assign tx %p\n", vqs[__VAPP_VQ_TX]);
  dtq->vqs[__VAPP_VQ_RX] = vqs[__VAPP_VQ_RX];
  dtq->vqs[__VAPP_VQ_TX] = vqs[__VAPP_VQ_TX];
}

#define virtio_vapp_dtq_lock_rx(_dtq) \
  mutex_lock(&(_dtq)->mutex[__VAPP_VQ_RX])
#define virtio_vapp_dtq_unlock_rx(_dtq) \
  mutex_unlock(&(_dtq)->mutex[__VAPP_VQ_RX])
#define virtio_vapp_dtq_lock_tx(_dtq) \
  mutex_lock(&(_dtq)->mutex[__VAPP_VQ_TX])
#define virtio_vapp_dtq_unlock_tx(_dtq) \
  mutex_unlock(&(_dtq)->mutex[__VAPP_VQ_TX])

#define __virtio_vapp_dtq_set_queue_run(_dtq, _vq) \
  do { \
    (_dtq)->vq_run[(_vq)] = true; \
  } while(0)

#define __virtio_vapp_dtq_set_queue_norun(_dtq, _vq) \
  do { \
    (_dtq)->vq_run[(_vq)] = false; \
  } while(0)

#define __virtio_vapp_dtq_is_queue_run(_dtq, _vq) \
  ((_dtq)->vq_run[(_vq)])

#define virtio_vapp_dtq_is_rx_run(_dtq) \
  __virtio_vapp_dtq_is_queue_run((_dtq), __VAPP_VQ_RX)
#define virtio_vapp_dtq_is_tx_run(_dtq) \
  __virtio_vapp_dtq_is_queue_run((_dtq), __VAPP_VQ_TX)
#define virtio_vapp_dtq_set_rx_run(_dtq) \
  __virtio_vapp_dtq_set_queue_run((_dtq), __VAPP_VQ_RX)
#define virtio_vapp_dtq_set_rx_norun(_dtq) \
  __virtio_vapp_dtq_set_queue_norun((_dtq), __VAPP_VQ_RX)
#define virtio_vapp_dtq_set_tx_run(_dtq) \
  __virtio_vapp_dtq_set_queue_run((_dtq), __VAPP_VQ_TX)
#define virtio_vapp_dtq_set_tx_norun(_dtq) \
  __virtio_vapp_dtq_set_queue_norun((_dtq), __VAPP_VQ_TX)

static inline void virtio_vapp_dtq_start(struct virtio_vapp_dtq* dtq)
{
  virtio_vapp_dtq_lock_rx(dtq);
  virtio_vapp_dtq_set_rx_run(dtq);
  virtio_vapp_dtq_unlock_rx(dtq);

  virtio_vapp_dtq_lock_tx(dtq);
  virtio_vapp_dtq_set_tx_run(dtq);
  virtio_vapp_dtq_unlock_tx(dtq);
}

static inline void virtio_vapp_dtq_stop(struct virtio_vapp_dtq* dtq)
{
  virtio_vapp_dtq_lock_rx(dtq);
  virtio_vapp_dtq_set_rx_norun(dtq);
  virtio_vapp_dtq_unlock_rx(dtq);

  virtio_vapp_dtq_lock_tx(dtq);
  virtio_vapp_dtq_set_tx_norun(dtq);
  virtio_vapp_dtq_unlock_tx(dtq);
}

static inline void __virtio_vapp_dtq_init_common(struct virtio_vapp_dtq* dtq)
{
  mutex_init(&dtq->mutex[__VAPP_VQ_RX]);
  mutex_init(&dtq->mutex[__VAPP_VQ_TX]);
  virtio_vapp_dtq_set_rx_norun(dtq);
  virtio_vapp_dtq_set_tx_norun(dtq);
  atomic_set(&dtq->queued_replies, 0);
  dtq->rx_bufs = 0;
  dtq->max_rx_bufs = 0;
}

void virtio_vapp_dtq_ctrl_tx_kick(struct virtqueue* vq);
void virtio_vapp_dtq_ctrl_rx_kick(struct virtqueue* vq);
void virtio_vapp_dtq_data_tx_kick(struct virtqueue* vq);
void virtio_vapp_dtq_data_rx_kick(struct virtqueue* vq);

void virtio_vapp_dtq_ctrl_tx_work(struct work_struct* work);
void virtio_vapp_dtq_ctrl_rx_work(struct work_struct* work);
void virtio_vapp_dtq_data_tx_work(struct work_struct* work);
void virtio_vapp_dtq_data_rx_work(struct work_struct* work);
void virtio_vapp_send_data_work(struct work_struct* work);
void virtio_vapp_send_ctrl_work(struct work_struct* work);

void virtio_vapp_dtq_ctrl_rx_fill(struct virtio_vapp_dtq* dtq);
void virtio_vapp_dtq_data_rx_fill(struct virtio_vapp_dtq* dtq);

static inline void virtio_vapp_dtq_set_queue(struct virtio_vapp_dtq* dtq,struct vapp_send_queue* queue)
{
  dtq->queue = queue;
}

static inline void virtio_vapp_dtq_init_ctrl(struct virtio_vapp_dtq* dtq)
{
  __virtio_vapp_dtq_init_common(dtq);
  INIT_WORK(&dtq->rx_work, virtio_vapp_dtq_ctrl_rx_work);
  INIT_WORK(&dtq->tx_work, virtio_vapp_dtq_ctrl_tx_work);
  INIT_WORK(&dtq->send_work, virtio_vapp_send_ctrl_work);
}

static inline void virtio_vapp_dtq_init_data(struct virtio_vapp_dtq* dtq)
{
  __virtio_vapp_dtq_init_common(dtq);
  INIT_WORK(&dtq->rx_work, virtio_vapp_dtq_data_rx_work);
  INIT_WORK(&dtq->tx_work, virtio_vapp_dtq_data_tx_work);
  INIT_WORK(&dtq->send_work, virtio_vapp_send_data_work);
}

static inline void virtio_vapp_dtq_flush_work(struct virtio_vapp_dtq* dtq)
{
  flush_work(&dtq->rx_work);
  flush_work(&dtq->tx_work);
  flush_work(&dtq->send_work);
}

static inline void __virtio_vapp_ctrl_dtq_clean_virtqueue(struct virtio_vapp_dtq* dtq, int queue)
{
  struct vapp_send_ctrl_data_packet* packet;
  while ((packet = virtqueue_detach_unused_buf(dtq->vqs[queue])))
    vapp_send_ctrl_data_packet_free(packet);
}
static inline void virtio_vapp_ctrl_dtq_clean_rx_virtqueue(struct virtio_vapp_dtq* dtq)
{
  virtio_vapp_dtq_lock_rx(dtq);
  __virtio_vapp_ctrl_dtq_clean_virtqueue(dtq, __VAPP_VQ_RX);
  virtio_vapp_dtq_unlock_rx(dtq);
}
static inline void virtio_vapp_ctrl_dtq_clean_tx_virtqueue(struct virtio_vapp_dtq* dtq)
{
  virtio_vapp_dtq_lock_tx(dtq);
  __virtio_vapp_ctrl_dtq_clean_virtqueue(dtq, __VAPP_VQ_TX);
  virtio_vapp_dtq_unlock_tx(dtq);
}
static inline void virtio_vapp_ctrl_dtq_clean_virtqueue(struct virtio_vapp_dtq* dtq)
{
  virtio_vapp_ctrl_dtq_clean_rx_virtqueue(dtq);
  virtio_vapp_ctrl_dtq_clean_tx_virtqueue(dtq);
}


static inline void __virtio_vapp_data_dtq_clean_virtqueue(struct virtio_vapp_dtq* dtq, int queue)
{
  struct vapp_send_data_packet* packet;
  while ((packet = virtqueue_detach_unused_buf(dtq->vqs[queue])))
    vapp_send_data_packet_free(packet);
}
static inline void virtio_vapp_data_dtq_clean_rx_virtqueue(struct virtio_vapp_dtq* dtq)
{
  virtio_vapp_dtq_lock_rx(dtq);
  __virtio_vapp_data_dtq_clean_virtqueue(dtq, __VAPP_VQ_RX);
  virtio_vapp_dtq_unlock_rx(dtq);
}
static inline void virtio_vapp_data_dtq_clean_tx_virtqueue(struct virtio_vapp_dtq* dtq)
{
  virtio_vapp_dtq_lock_tx(dtq);
  __virtio_vapp_data_dtq_clean_virtqueue(dtq, __VAPP_VQ_TX);
  virtio_vapp_dtq_unlock_tx(dtq);
}

static inline void virtio_vapp_data_dtq_clean_virtqueue(struct virtio_vapp_dtq* dtq)
{
  virtio_vapp_data_dtq_clean_rx_virtqueue(dtq);
  virtio_vapp_data_dtq_clean_tx_virtqueue(dtq);
}

#endif // ! __VIRTIO_VAPP_DTQ_H__
